package com.powernode.ssm.service;

import com.powernode.ssm.config.DynamicDataSourceConfigLogger;
import com.powernode.ssm.utils.DynamicDataSourceUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 数据库元数据服务类（获取表结构、列信息等）
 */
@Slf4j
@Service
public class DatabaseMetaDataService {

    @Autowired
    DynamicDataSourceUtil dynamicDataSourceUtil;


    /**
     * 获取指定数据源中 指定数据库（从 URL 自动解析）下的所有表名（只包括普通表，过滤视图）
     */
    public List<String> getTableNames(String dataSourceName) throws SQLException {
        // 1. 从 DynamicDataSourceConfigLogger 中获取该数据源的 JDBC URL
        String url = dynamicDataSourceUtil.getURL(dataSourceName);

        if (url == null || url.trim().isEmpty()) {
            throw new IllegalArgumentException("无法获取数据源 '" + dataSourceName + "' 的 JDBC URL，请检查配置");
        }

        // 2. ✅ 使用 Java 8 方式从 URL 中解析数据库名（即 schema / 数据库）
        // 典型 URL 格式：jdbc:mysql://localhost:3306/jdbc_db?xxx
        // 我们要提取 jdbc_db 部分
        String databaseName = extractDatabaseNameFromJdbcUrl(url);

        if (databaseName == null || databaseName.trim().isEmpty()) {
            throw new IllegalArgumentException("无法从数据源 '" + dataSourceName + "' 的 JDBC URL 中解析出数据库名（schema）: " + url);
        }

        log.debug("[🔍] 数据源 '{}' 对应的数据库（schema）名为: {}", dataSourceName, databaseName);

        // 3. 获取该数据源的连接，并查询该数据库下的所有普通表
        try (Connection conn = dynamicDataSourceUtil.getConnection(dataSourceName)) {
            DatabaseMetaData metaData = conn.getMetaData();

            // 只查询普通表，过滤视图
            String[] tableTypes = {"TABLE"};

            // ✅ 重点：将解析出来的数据库名（schema）作为 catalog 传入，限定查询范围
            ResultSet rs = metaData.getTables(databaseName, null, "%", tableTypes);

            List<String> tables = new ArrayList<>();
            while (rs.next()) {
                String tableName = JdbcUtils.commonDatabaseName(rs.getString("TABLE_NAME"));
                tables.add(tableName);
            }

            log.debug("[✅] 数据源 '{}' 下的数据库 '{}' 中查询到 {} 个表", dataSourceName, databaseName, tables.size());
            return tables;
        }
    }

    /**
     * ✅ 从 JDBC URL 中解析出数据库名（即 schema / 数据库名称），适用于 MySQL / MariaDB
     * URL 示例: jdbc:mysql://localhost:3306/jdbc_db?serverTimezone=UTC
     * 返回: jdbc_db
     */
    private String extractDatabaseNameFromJdbcUrl(String jdbcUrl) {
        try {
            // 1. 去掉 jdbc: 前缀
            String normalizedUrl = jdbcUrl.replaceFirst("^jdbc:", "");

            // 2. 按第一个冒号或斜杠分割，得到协议后的部分，例如：mysql://localhost:3306/jdbc_db?xxx
            int firstSlashIndex = normalizedUrl.indexOf('/');
            if (firstSlashIndex == -1) {
                return null; // 无法解析
            }

            // 3. 取 '/' 之后的部分：localhost:3306/jdbc_db?xxx
            String afterProtocol = normalizedUrl.substring(firstSlashIndex + 1);

            // 4. 按 '?' 分割，得到路径部分（去掉参数）
            int questionMarkIndex = afterProtocol.indexOf('?');
            String pathPart = questionMarkIndex == -1
                    ? afterProtocol
                    : afterProtocol.substring(0, questionMarkIndex);

            // 5. 按 '/' 分割路径，取最后一部分即为数据库名
            String[] pathSegments = pathPart.split("/");
            if (pathSegments.length == 0) {
                return null;
            }

            // 最后一段就是数据库名，比如 jdbc_db
            return pathSegments[pathSegments.length - 1];
        } catch (Exception e) {
            log.warn("[⚠️] 解析 JDBC URL 出错: {}, 错误: {}", jdbcUrl, e.getMessage());
            return null;
        }
    }

    /**
     * 获取表列信息（字段名、类型、是否主键、是否可为空）
     */
    public List<ColumnInfo> getColumnInfos(String dataSourceName, String tableName) throws SQLException {
        try (Connection conn = dynamicDataSourceUtil.getConnection(dataSourceName)) {
            DatabaseMetaData metaData = conn.getMetaData();

            // 获取列信息
            ResultSet colRs = metaData.getColumns(null, null, tableName, null);
            List<ColumnInfo> columns = new ArrayList<>();
            while (colRs.next()) {
                ColumnInfo column = new ColumnInfo();
                column.setName(JdbcUtils.commonDatabaseName(colRs.getString("COLUMN_NAME")));
                column.setType(JdbcUtils.commonDatabaseName(colRs.getString("TYPE_NAME")));
                column.setSize(colRs.getInt("COLUMN_SIZE"));
                column.setNullable(colRs.getInt("NULLABLE") == DatabaseMetaData.columnNullable);
                columns.add(column);
            }

            // 获取主键信息
            ResultSet pkRs = metaData.getPrimaryKeys(null, null, tableName);
            Set<String> primaryKeys = new HashSet<>();
            while (pkRs.next()) {
                primaryKeys.add(JdbcUtils.commonDatabaseName(pkRs.getString("COLUMN_NAME")));
            }

            // 标记主键列
            columns.forEach(col -> col.setPrimaryKey(primaryKeys.contains(col.getName())));
            return columns;
        }
    }

    /**
     * 列信息实体类
     */
    public static class ColumnInfo {
        private String name;       // 字段名
        private String type;       // 数据库类型
        private int size;          // 字段长度
        private boolean nullable;  // 是否可为空
        private boolean primaryKey;// 是否主键

        // getter/setter
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getType() { return type; }
        public void setType(String type) { this.type = type; }
        public int getSize() { return size; }
        public void setSize(int size) { this.size = size; }
        public boolean isNullable() { return nullable; }
        public void setNullable(boolean nullable) { this.nullable = nullable; }
        public boolean isPrimaryKey() { return primaryKey; }
        public void setPrimaryKey(boolean primaryKey) { this.primaryKey = primaryKey; }
    }
}